package org.mj.im.common.distribute;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONUtil;
import org.mj.im.common.distribute.conversion.TypeConversion;

import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;

public interface DistributeCacheService {
    void set(String key,Object value);
    void set(String key, Object value, long timeout, TimeUnit timeUnit);
    Boolean expire(String key, long timeout, TimeUnit timeUnit);
    Long addSet(String key,Object ...values);
    Boolean isMemberSet(String key,Object value);
    Set<String> memberSet(String key);
    Long removeSet(String key,Object ...values);
    Long sizeSet(String key);
    void setWithLogicalExpire(String key,Object value,long timeout, TimeUnit timeUnit);
    String get(String key);
    <T> T getObject(String key, Class<T> clazz);
    List<String> multiGet(Collection<String> keys);
    Set<String> keys(String pattern);
    Boolean delete(String key);

    /**
     * 带参数查询对象和简单类型数据，防止缓存穿透
     * @param keyPrefix 缓存Key的前缀
     * @param id 缓存的业务标识
     * @param type 缓存的实际对象类型
     * @param dbFallback 查询数据库的Function函数
     * @param timeout 缓存的时长
     * @param timeUnit 时间单位
     * @return 返回业务数据
     * @param <R> 结果类型
     * @param <ID> 查询数据库参数类型
     */
    <R,ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallback,Long timeout,
                                  TimeUnit timeUnit);

    /**
     * 不带参数查询对象和简单类型数据（是否需要参数查询db数据），防止缓存穿透
     * @param keyPrefix key的前缀
     * @param type 缓存的实际数据类型
     * @param dbFallback 无参数查询数据库数据
     * @param timeout 缓存的时长
     * @param timeUnit 时间单位
     * @return 业务数据
     * @param <R> 结果泛型
     */
    <R> R queryWithPassThroughWithoutArgs(String keyPrefix, Class<R> type, Supplier<List<R>> dbFallback,
                                          Long timeout, TimeUnit timeUnit);

    /**
     * 带参数查询集合数据，防止缓存穿透
     * @param keyPrefix 缓存key的前缀
     * @param id 缓存的业务标识
     * @param type 缓存的实际对象类型
     * @param dbFallback 查询数据库的Function函数
     * @param timeout 缓存的时长
     * @param timeUnit 时间单位
     * @return 返回业务数据
     * @param <R> 结果类型
     * @param <ID> 业务参数类型
     */
    <R,ID> List<R> queryWithPassThroughList(String keyPrefix,ID id,Class<R> type,Function<ID,List<R>> dbFallback,
                                            Long timeout,TimeUnit timeUnit);

    /**
     * 不带参数查询集合数据，防止缓存穿透
     * @param keyPrefix 缓存key的前缀
     * @param type 缓存的实际对接类型
     * @param dbFallback 无参数查询数据库数据
     * @param timeout 缓存的时长
     * @param timeUnit 时间单位
     * @return 业务数据
     * @param <R> 结果类型
     */
    <R> List<R> queryWithPassThroughListWithoutArgs(String keyPrefix,Class<R> type,Supplier<List<R>> dbFallback,
                                                Long timeout,TimeUnit timeUnit);

    /**
     * 不带参数查询数据，按照逻辑过期时间读取缓存数据，新开线程重建缓存，其他线程直接返回逻辑过期数据，不占用资源
     * @param keyPrefix 缓存key的前缀
     * @param id 缓存业务标识，查询数据库的参数
     * @param type 缓存的实际对象类型
     * @param dbFallback 查询数据库的Function函数
     * @param timeout 缓存逻辑过期时长
     * @param timeUnit 时间单位
     * @return 业务数据
     * @param <R> 结果数据类型
     * @param <ID> 查询数据库参数类型
     */
    <R,ID> R queryWithLogicExpire(String keyPrefix,ID id,Class<R> type,Function<ID,R> dbFallback,
                                  Long timeout,TimeUnit timeUnit);

    /**
     * 不带参数查询数据，按照逻辑过期时间读取缓存数据，新开线程重建缓存，其他线程知己返回逻辑过期数据，不占用资源
     * @param keyPrefix 缓存key的前缀
     * @param type 缓存的实际对象类型
     * @param dbFallback 无参数查询数据库类型
     * @param timeout 缓存时长
     * @param timeUnit 时间单位
     * @return 返回业务数据
     * @param <R> 结果类型
     */
    <R> R queryWithLogicalExpireWithoutArgs(String keyPrefix,Class<R> type,Supplier<R> dbFallback,
                                            Long timeout,TimeUnit timeUnit);

    /**
     * 带参数查询结合数据，安装逻辑过期时间读取缓存数据，新开线程创建缓存，其他线程直接返回逻辑过期数据，不占用资源
     * @param keyPrefix 缓存key的前缀
     * @param id 缓存业务标识，查询db的参数
     * @param type 缓存的实际对象类型
     * @param dbFallback 查询数据库的Function函数
     * @param timeout 缓存逻辑过期时间
     * @param timeUnit 时间单位
     * @return 返回业务数据
     * @param <R> 业务数据类型
     * @param <ID> 查询参数类型
     */
    <R,ID> List<R> queryWithLogicExpireList(String keyPrefix,ID id,Class<R> type,Function<ID,List<R>> dbFallback,
                                            Long timeout,TimeUnit timeUnit);

    /**
     * 不带参数查询集合数据，按照逻辑过期时间读取缓存数据，新开线程创建缓存，其他线程直接返回逻辑过期数据，占用资源
     * @param keyPrefix 缓存key的前缀
     * @param type 缓存的实际对象类型
     * @param dbFallback 无参数查询数据库数据
     * @param timeout 缓存的时长
     * @param timeUnit 时间单位
     * @return 返回业务数据
     * @param <R> 数据类型
     */
    <R> List<R> queryWithLogicalExpireListWithoutArgs(String keyPrefix,Class<R> type,Supplier<List<R>> dbFallback,
                                                      Long timeout,TimeUnit timeUnit);

    /**
     * 带参数查询数据，按照互斥锁方式获取缓存数据，同一时刻只有一个线程访问数据库，其他线程访问不到数据进行重试
     * @param keyPrefix 缓存key的前缀
     * @param id 缓存业务标识
     * @param type 缓存的实际对象类型
     * @param dbFallback 查询数据库的Function函数
     * @param timeout 缓存时长
     * @param timeUnit 时间单位
     * @return 返回业务数据
     * @param <R> 结果数据类型
     * @param <ID> 查询数据库参数类型
     */
    <R,ID> R queryWithMutex(String keyPrefix,ID id,Class<R> type,Function<ID,R> dbFallback,
                            Long timeout,TimeUnit timeUnit);

    /**
     * 不带参数查询数据，按照互斥锁方式获取缓存数据，同一时刻只有一个线程访问数据库，其他线程访问不到数据进行重试
     * @param keyPrefix 缓存key的前缀
     * @param type 缓存的实际对象类型
     * @param dbFallback 查询数据库的Function函数
     * @param timeout 缓存时长
     * @param timeUnit 时间单位
     * @return 返回业务数据
     * @param <R> 结果数据类型
     */
    <R> R queryWithMutexWithoutArgs(String keyPrefix,Class<R> type,Supplier<R> dbFallback,
                            Long timeout,TimeUnit timeUnit);
    /**
     * 带参数查询数据，按照互斥锁方式获取缓存数据，同一时刻只有一个线程访问数据库，其他线程访问不到数据进行重试
     * @param keyPrefix 缓存key的前缀
     * @param id 缓存业务标识
     * @param type 缓存的实际对象类型
     * @param dbFallback 查询数据库的Function函数
     * @param timeout 缓存时长
     * @param timeUnit 时间单位
     * @return 返回业务数据
     * @param <R> 结果数据类型
     * @param <ID> 查询数据库参数类型
     */
    <R,ID> List<R> queryWithMutexList(String keyPrefix,ID id,Class<R> type,Function<ID,List<R>> dbFallback,
                            Long timeout,TimeUnit timeUnit);

    /**
     * 不带参数查询数据，按照互斥锁方式获取缓存数据，同一时刻只有一个线程访问数据库，其他线程访问不到数据进行重试
     * @param keyPrefix 缓存key的前缀
     * @param type 缓存的实际对象类型
     * @param dbFallback 查询数据库的Function函数
     * @param timeout 缓存时长
     * @param timeUnit 时间单位
     * @return 返回业务数据
     * @param <R> 结果数据类型
     */
    <R> List<R> queryWithMutexListWithoutArgs(String keyPrefix,Class<R> type,Supplier<List<R>> dbFallback,
                            Long timeout,TimeUnit timeUnit);
    default <R> R getResult(Object obj,Class<R> type){
        if (obj == null){
            return null;
        }
        if (TypeConversion.isSimpleType(obj)){
            return Convert.convert(type,obj);
        }
        return JSONUtil.toBean(JSONUtil.toJsonStr(obj),type);
    }
    default <R> List<R> getResultList(String str,Class<R> type){
        if (StrUtil.isEmpty(str)){
            return null;
        }
        return JSONUtil.toList(JSONUtil.parseArray(str),type);
    }
    default String getKey(String key){
        return getKey(key,null);
    }
    default <ID> String getKey(String keyPrefix,ID id){
        if (id == null){
            return keyPrefix;
        }
        String key = "";
        if (TypeConversion.isSimpleType(id)){
            key = StrUtil.toString(id);
        }else{
            key = MD5.create().digestHex(JSONUtil.toJsonStr(id));
        }
        if (StrUtil.isEmpty(key)){
            key = "";
        }
        return keyPrefix.concat(key);
    }
    default String getValue(Object value){
        return TypeConversion.isSimpleType(value) ? String.valueOf(value) : JSONUtil.toJsonStr(value);
    }
}
